﻿using System.Collections.Generic;
using System.Linq;
using System;

namespace PathSplitterUtil
{
    public static class EnumerableUtil
    {
        public static ISet<T> ToSet<T>(this IEnumerable<T> xs)
        {
            return new HashSet<T>(xs);
        }
        public static T[] Singleton<T>(this T item)
        {
            return new T[] { item };
        }
        public static IEnumerable<T> Cons<T>(this T head, IEnumerable<T> rest)
        {
            yield return head;
            foreach (T x in rest)
            {
                yield return x;
            }
        }
        public static IEnumerable<T> Concat<T>(this IEnumerable<IEnumerable<T>> xss)
        {
            return (from xs in xss
                    from x in xs
                    select x);
        }
        public static IList<T> ToIList<T>(this IEnumerable<T> xs)
        {
            return (IList<T>)xs.ToList();
        }

        public static IEnumerable<IList<T>> CartesianProduct<T>(
            this IEnumerable<IEnumerable<T>> left, IEnumerable<IEnumerable<T>> right)
        {
            return (from l in left
                    select (from r in right
                            select l.Concat(r).ToList()))
                   .Concat();
        }
        public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
            this IEnumerable<IEnumerable<IEnumerable<T>>> xss)
        {
            IEnumerable<IEnumerable<T>> tmp = new List<T>().Singleton();
            foreach (var xs in xss)
            {
                tmp = tmp.CartesianProduct(xs);
            }
            return tmp;
        }
        public static IEnumerable<IList<T>> CartesianProductSingletons<T>(
            this IEnumerable<IEnumerable<T>> xss)
        {
            IEnumerable<IList<T>> tmp = new List<T>().Singleton();
            foreach (var xs in xss)
            {
                tmp = tmp.CartesianProduct(xs.Select(x => x.Singleton()));
            }
            return tmp;
        }
        public static IEnumerable<T[]> CartesianProductSingletons<T>(
            this List<SortedSet<T>> xss)
        {
            int len = xss.Count;
            if (len == 0)
            {
                yield return new T[0];
                yield break;
            }
            int[] counts = xss.Select(xs => xs.Count).ToArray();
            int resLen = counts.Product();

            for (int i = 0; i < resLen; i++)
            {
                T[] current = new T[len];
                int tmp = i;
                for (int j = 0; j < len; j++)
                {
                    current[j] = xss[j].ElementAt(tmp % counts[j]);
                    tmp /= counts[j];
                }
                yield return current;
            }
        }

        public static decimal Product(this IEnumerable<decimal> xs)
        {
            return xs.Aggregate((x, y) => x * y);
        }
        public static int Product(this IEnumerable<int> xs)
        {
            return xs.Aggregate((x, y) => x * y);
        }

        // From http://stackoverflow.com/a/1262619
        public static void Shuffle<T>(this IList<T> list)
        {
            Random rng = new Random();
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }
    }
}
